/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.raflik.queryxml.ui.tasks;

import pl.raflik.queryxml.ui.tasks.io.CommonQueryInput;
import pl.raflik.queryxml.ui.tasks.io.CommonQueryOutput;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.openide.filesystems.FileObject;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import pl.raflik.queryxml.langservice.QueryLanguage;
import pl.raflik.queryxml.langservice.QueryResolverException;
import pl.raflik.queryxml.ui.UITransfer;

/**
 *
 * @author raflik
 */
public abstract class CommonQueryThread<I extends CommonQueryInput, O extends CommonQueryOutput> extends AsyncOperationThread<I, O> {

    private Map<String, Document> cache;
    private Document currentDoc;

    public CommonQueryThread(UITransfer<I, O> transfer, Map<String, Document> cache) {
        super(transfer);
        this.cache = cache;
    }

    protected CommonQueryOutput validateQuery(CommonQueryInput input) {
        final QueryLanguage ql = input.getQueryLanguage();
        final String query = input.getQuery();
        final FileObject sourceFO = input.getFileObject();

        if (ql == null) {
            return new CommonQueryOutput(QueryResultCode.NO_QUERY_LANG);
        }

        if (query == null || query.equals("")) {
            return new CommonQueryOutput(QueryResultCode.QUERY_EMPTY);
        }

        if (!ql.isValid(query)) {
            return new CommonQueryOutput(QueryResultCode.QUERY_NOT_VALID);
        }

        if (sourceFO == null) {
            return new CommonQueryOutput(QueryResultCode.NO_INPUT_FILE);
        }


        String key = sourceFO.getPath();
        Document xmlDoc = null;
        if (cache.containsKey(key)) {
            xmlDoc = cache.get(key);
        }
        if (xmlDoc == null) {
            InputStream inputStream = null;
            try {
                inputStream = sourceFO.getInputStream();
            } catch (FileNotFoundException e) {
                return new CommonQueryOutput(QueryResultCode.INPUT_FILE_NOT_FOUND);
            }
            if (inputStream == null) {
                return new CommonQueryOutput(QueryResultCode.INPUT_READ_FAILED);
            }

            InputSource inputSource = new InputSource(inputStream);
            try {
                xmlDoc = XMLUtil.parse(inputSource, false, true, null, null);
            } catch (IOException e) {
                return new CommonQueryOutput(QueryResultCode.INPUT_READ_FAILED);
            } catch (SAXException e) {
                return new CommonQueryOutput(QueryResultCode.INPUT_FILE_MALFORMED_XML);
            }
            cache.put(key, xmlDoc);
        }
        currentDoc = xmlDoc;

        return new CommonQueryOutput(QueryResultCode.QUERY_OK);
    }

    protected CommonQueryOutput invokeQueryUnchecked(CommonQueryInput input) {
        if (currentDoc == null) {
            throw new IllegalStateException("currentDoc == null");
        }

        // process
        List<?> output = null;
        final String query = input.getQuery();
        final QueryLanguage ql = input.getQueryLanguage();

        try {
            output = ql.resolve(query, currentDoc, null);
        } catch (QueryResolverException e) {
            return new CommonQueryOutput(QueryResultCode.QUERY_FAILED);
        } finally {
            currentDoc = null;
        }

        return new CommonQueryOutput(QueryResultCode.QUERY_OK, output);

    }

    protected CommonQueryOutput performCommonOperation(CommonQueryInput input) {
        CommonQueryOutput output = validateQuery(input);
        if (output.getResultCode().isError()) {
            return output;
        }

        return invokeQueryUnchecked(input);
    }
}
